package com.deam.imsdk.net;

import android.util.Log;

import com.alibaba.fastjson.JSONObject;
import com.deam.imsdk.bean.UserInfo;
import com.deam.imsdk.utils.Constants;
import com.deam.imsdk.utils.UserUtil;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;

/**
 * Created by Administrator on 2018/4/18.
 */

public class TCPClient implements Runnable, MessageObserver {
    public static final String TAG = TCPClient.class.getSimpleName();
    public static final long AGAIN_CONNECT_TIME = 5000;//重新链接时间
    //信道选择器
    public static final int CONNECT_NO = 0;//断开
    public static final int CONNECT_IN = 1;//正在连接
    public static final int CONNECT_OK = 2;//已连接
    public static final String ENCODING = "UTF-8";
    private TCPProtocol mStateListener;
    // 超时时间，单位毫秒
    private static final int TIME_OUT = 3000;
    // 缓冲区大小
    public static final int bufferSize = 1024 * 10;
    // 信道选择器
    private Selector mSelector;
    // 与服务器通信的信道
    private SocketChannel socketChannel;
    // 要连接的服务器Ip地址
    private String hostname;
    // 要连接的远程服务器在监听的端口
    private int port;
    public static Charset charset = Charset.forName(ENCODING);
    private MessagePullProcessor msgPull = new MessagePullProcessor();
    private int mState = CONNECT_NO;
    /**
     * 单键实例
     */
    private static TCPClient mTcpClient;

    public static synchronized TCPClient instance() {
        if (mTcpClient == null) {
            mTcpClient = new TCPClient();
        }
        return mTcpClient;
    }

    public TCPClient() {
        msgPull.setMessageObserver(this);
    }

    /**
     * 链接远端地址
     *
     * @param remoteIp
     * @param port
     * @return
     */
    public void connect(String remoteIp, int port, TCPProtocol stateListener) {
        this.hostname = remoteIp;
        this.port = port;
        this.mStateListener = stateListener;
        new Thread(this).start();
    }

    //设置连接状态
    private void setState(int state) {
        this.mState = state;
        if (mStateListener != null) mStateListener.onUpdateConnect(state);
    }

    //获取连接状态
    public int getConnectState() {
        return mState;
    }

    /**
     * 关闭链接
     */
    public void close() {
        try {
            if (mSelector != null) {
                mSelector.close();
            }
            if (socketChannel != null) {
                socketChannel.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 重连
     *
     * @return
     */
    public void reConnect() {
        close();
        new Thread(this).start();
    }

    /**
     * 是否链接着
     *
     * @return
     */
    public boolean isConnect() {
        if (mState != CONNECT_OK) {
            return false;
        }
        return socketChannel.isConnected();
    }

    /**
     * 发送字符串到服务器
     *
     * @param json
     * @throws IOException
     */
    public void sendMsg(final JSONObject json) {
        new Thread() {
            @Override
            public void run() {
                try {
                    if (getConnectState() == CONNECT_OK) {
                        ByteBuffer writeBuffer = ByteBuffer.wrap((json.toJSONString() + MessagePullProcessor.MSG_END).getBytes(ENCODING));
                        socketChannel.write(writeBuffer);
                        return;
                    }
                } catch (IOException e) {
                    setState(CONNECT_NO);
                    close();
                }
                json.put(Constants.CODE, Constants.CODE_ERROR);
                json.put(Constants.MSG, Constants.ERROR_NONET);
                msgPull.addMessage(json.toJSONString() + MessagePullProcessor.MSG_END);
            }
        }.start();
    }

    @Override
    public void run() {
        // 打开监听信道并设置为非阻塞模式
        try {
            setState(CONNECT_IN);
            socketChannel = SocketChannel.open(new InetSocketAddress(hostname, port));
            socketChannel.configureBlocking(false);
            // 打开并注册选择器到信道
            mSelector = Selector.open();
            socketChannel.register(mSelector, SelectionKey.OP_READ);
            setState(CONNECT_OK);
            String token = UserUtil.getUser().getToken();
            if (token != null && token.length() > 0) {
                JSONObject json = new JSONObject();
                json.put(Constants.REQUEST, Constants.ACTION_LOGING);
                json.put(Constants.USER_ACTION, Constants.ACTION_LOGING);
                json.put(UserInfo.TOKEN, UserUtil.getUser().getToken());
                sendMsg(json);
            }
        } catch (IOException e) {
            setState(CONNECT_NO);
            close();
            return;
        }
        while (mState == CONNECT_OK) {
            dispatchEvent();
        }
    }

    private void dispatchEvent() {
        try {
            if (mSelector.select(TIME_OUT) == 0) return;
            Iterator<SelectionKey> ite = mSelector.selectedKeys().iterator();
            while (ite.hasNext()) {
                SelectionKey key = ite.next();
                // 删除已选的key,以防重复处理
                ite.remove();
                if (key.isReadable()) {
                    // 获得与客户端通信的信道
                    SocketChannel client = (SocketChannel) key.channel();
                    // 得到并清空缓冲区
                    ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
                    // 读取信息获得读取的字节数
                    client.read(buffer);
                    buffer.flip();
                    // 将字节转化为为UTF-16的字符串
                    String receivedString = charset.decode(buffer).toString();
                    Log.d(TAG, receivedString);
                    msgPull.addMessage(receivedString);
                    buffer.clear();
                }
                // 设置为下一次读取或是写入做准备
                key.interestOps(SelectionKey.OP_READ);
            }
        } catch (Exception e) {
            //断开连接
            setState(CONNECT_NO);
            close();
        }
    }

    @Override
    public void doHandleMessage(JSONObject json) {
        mStateListener.doHandleMessage(json);
    }
}
